package zlib

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

// EncodeURL 转义url或转义其他字符
func EncodeURL(_url string) string {
	return url.QueryEscape(_url)
}

// DecodeURL 解义url
func DecodeURL(_url string) (string, error) {
	return url.QueryUnescape(_url)
}

// GetUrlParam 获取url中的参数（非解码）
func GetUrlParam(_url string, _key string) (value string) {
	u, err := url.Parse(_url)
	values := u.Query()
	if err != nil {
		value = ""
	} else {
		value = values.Get(_key)
	}
	return
}

// 发送GET请求
// url：         请求地址
// response：    请求返回的内容
func Get(url string) string {
	// 超时时间：5秒
	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Get(url)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	var buffer [512]byte
	result := bytes.NewBuffer(nil)
	for {
		n, err := resp.Body.Read(buffer[0:])
		result.Write(buffer[0:n])
		if err != nil && err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}
	}

	return result.String()
}

// 发送POST请求
// url：         请求地址
// data：        POST请求提交的数据
// contentType： 请求体格式，如：application/json
// content：     请求放回的内容
func Post(url string, data interface{}, contentType string) string {
	// 超时时间：5秒
	client := &http.Client{Timeout: 5 * time.Second}
	jsonStr, _ := json.Marshal(data)
	resp, err := client.Post(url, contentType, bytes.NewBuffer(jsonStr))
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	result, _ := ioutil.ReadAll(resp.Body)
	return string(result)
}

func simpleDownload(url string, localFile string) string {
	// Get the data
	resp, err := http.Get(url)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	// 创建一个文件用于保存
	out, err := os.Create(localFile)
	if err != nil {
		panic(err)
	}
	defer out.Close()

	// 然后将响应流和文件流对接起来
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return ""
	}
	return localFile
}

func ReferDownload(url string, site string, localFile string) string {
	client := http.Client{}
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		log.Println(err)
		return ""
	}
	// 添加请求头
	req.Header.Add("referer", site)
	req.Header.Add("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53")
	// 发送请求
	resp, err := client.Do(req)
	if err != nil {
		log.Println("err")
		return ""
	}
	defer resp.Body.Close()

	// 创建一个文件用于保存
	out, err := os.Create(localFile)
	if err != nil {
		return ""
	}
	defer out.Close()

	// 然后将响应流和文件流对接起来
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		return ""
	}
	return localFile
}

// buildQueryMap 创建URL Query参数字典.
// result 为结果字典;keys 为键数组;value为键值.
func BuildQueryMap(result map[string]interface{}, keys []string, value interface{}) error {
	length := len(keys)
	// trim ',"
	key := strings.Trim(keys[0], "',\"")
	if length == 1 {
		result[key] = value
		return nil
	}

	// The end is slice. like f[], f[a][]
	if keys[1] == "" && length == 2 {
		if key == "" {
			return nil
		}
		val, ok := result[key]
		if !ok {
			result[key] = []interface{}{value}
			return nil
		}
		children, ok := val.([]interface{})
		if !ok {
			return fmt.Errorf("[buildQueryMap] expected type '[]interface{}' for key '%s', but got '%T'", key, val)
		}
		result[key] = append(children, value)
		return nil
	}

	// The end is slice + map. like f[][a]
	if keys[1] == "" && length > 2 && keys[2] != "" {
		val, ok := result[key]
		if !ok {
			result[key] = []interface{}{}
			val = result[key]
		}
		children, ok := val.([]interface{})
		if !ok {
			return fmt.Errorf("[buildQueryMap] expected type '[]interface{}' for key '%s', but got '%T'", key, val)
		}
		if l := len(children); l > 0 {
			if child, ok := children[l-1].(map[string]interface{}); ok {
				if _, ok := child[keys[2]]; !ok {
					_ = BuildQueryMap(child, keys[2:], value)
					return nil
				}
			}
		}
		child := map[string]interface{}{}
		_ = BuildQueryMap(child, keys[2:], value)
		result[key] = append(children, child)

		return nil
	}

	// map. like f[a], f[a][b]
	val, ok := result[key]
	if !ok {
		result[key] = map[string]interface{}{}
		val = result[key]
	}
	children, ok := val.(map[string]interface{})
	if !ok {
		return fmt.Errorf("[buildQueryMap] expected type 'map[string]interface{}' for key '%s', but got '%T'", key, val)
	}

	return BuildQueryMap(children, keys[1:], value)
}

// if rawUrl=http://www.google.com:8080/news/index.asp?boardID=520&page=1&page=2#name and key="page" will get "1"
func RawURLGetParam(rawUrl, key string) (string, error) {
	stUrl, err := url.Parse(rawUrl)
	if err != nil {
		return "", err
	}

	m := stUrl.Query()
	if v, ok := m[key]; ok && len(v) > 0 {
		return v[0], nil
	}
	return "", errors.New("no param")
}

// if rawUrl=http://www.google.com:8080/news/index.asp?boardID=520&page=1&page=2#name and key=page will get [1 2]
func RawURLGetParams(rawUrl, key string) ([]string, error) {
	stUrl, err := url.Parse(rawUrl)
	if err != nil {
		return nil, err
	}

	m := stUrl.Query()
	if v, ok := m[key]; ok {
		return v, nil
	}
	return nil, errors.New("no param")
}

// if rawUrl=http://www.google.com:8080/news/index.asp?boardID=520&page=1&page=2#name will get map[boardID:[520] page:[1 2]]
func RawURLGetAllParams(rawUrl string) (map[string][]string, error) {
	stUrl, err := url.Parse(rawUrl)
	if err != nil {
		return nil, err
	}

	m := stUrl.Query()
	return m, nil
}

// if rawUrl=http://www.google.com:8080/news/index.asp?boardID=520&page=1&page=2#name and key=page value=3
// will get http://www.google.com:8080/news/index.asp?boardID=520&page=1&page=2&page=3#name
func RawURLAddParam(rawUrl, key, value string) string {
	stUrl, err := url.Parse(rawUrl)
	if err != nil {
		return rawUrl
	}

	m := stUrl.Query()
	m.Add(key, value)
	stUrl.RawQuery = m.Encode()
	return stUrl.String()
}

func RawURLAddParams(rawUrl string, params map[string]string) string {
	stUrl, err := url.Parse(rawUrl)
	if err != nil {
		return rawUrl
	}

	m := stUrl.Query()
	for k, v := range params {
		m.Add(k, v)
	}
	stUrl.RawQuery = m.Encode()
	return stUrl.String()
}

// if rawUrl=http://www.google.com:8080/news/index.asp?boardID=520&page=1&page=2#name and key=page
// will get http://www.google.com:8080/news/index.asp?boardID=520#name
func RawURLDelParam(rawUrl, key string) string {
	stUrl, err := url.Parse(rawUrl)
	if err != nil {
		return rawUrl
	}

	m := stUrl.Query()
	m.Del(key)
	stUrl.RawQuery = m.Encode()
	return stUrl.String()
}

func RawURLDelParams(rawUrl string, keys []string) string {
	stUrl, err := url.Parse(rawUrl)
	if err != nil {
		return rawUrl
	}

	m := stUrl.Query()
	for _, v := range keys {
		m.Del(v)
	}
	stUrl.RawQuery = m.Encode()
	return stUrl.String()
}

// if rawUrl=http://www.google.com:8080/news/index.asp?boardID=520&page=1&page=2#name and key=page value=3
// will get http://www.google.com:8080/news/index.asp?boardID=520&page=3#name
func RawURLSetParam(rawUrl, key, value string) string {
	stUrl, err := url.Parse(rawUrl)
	if err != nil {
		return rawUrl
	}

	m := stUrl.Query()
	m.Set(key, value)
	stUrl.RawQuery = m.Encode()
	return stUrl.String()
}

func RawURLSetParams(rawUrl string, params map[string]string) string {
	stUrl, err := url.Parse(rawUrl)
	if err != nil {
		return rawUrl
	}

	m := stUrl.Query()
	for k, v := range params {
		m.Set(k, v)
	}
	stUrl.RawQuery = m.Encode()
	return stUrl.String()
}

// if rawUrl=http://www.google.com:8080/news/index.asp?boardID=520&page=1&page=2#name
// will get www.google.com
func RawUrlGetDomain(rawUrl string) string {
	stUrl, err := url.Parse(rawUrl)
	if err != nil {
		return ""
	}
	return stUrl.Hostname()
}

// if rawUrl=http://www.google.com:8080/news/index.asp?boardID=520&page=1&page=2#name
// will get 8080
func RawUrlGetPort(rawUrl string) string {
	stUrl, err := url.Parse(rawUrl)
	if err != nil {
		return ""
	}
	return stUrl.Port()
}

// RawQueryGetParam get the specified key parameter from query string
// rawquery is encoded query values without '?'. key is parameter name
// e.g. if query is "a=dog&b=tiger" and key is "a" will return dog
func RawQueryGetParam(rawquery, key string) (string, error) {
	queries, err := url.ParseQuery(rawquery)
	if err != nil {
		return "", err
	}

	if v, ok := queries[key]; ok && len(v) > 0 {
		return v[0], nil
	}
	return "", errors.New("no param")
}

// RawQueryGetParams get the specified key parameters from query string
// rawquery is encoded query values without '?'. key is parameter name
// e.g. if query is "a=dog&a=cat&b=tiger" and key is "a" will return [dog cat]
func RawQueryGetParams(rawquery, key string) ([]string, error) {
	queries, err := url.ParseQuery(rawquery)
	if err != nil {
		return nil, err
	}

	if v, ok := queries[key]; ok && len(v) > 0 {
		return v, nil
	}
	return nil, errors.New("no param")
}
